package com.yangjie.autotest.testcase;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import org.apache.commons.beanutils.MethodUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.yangjie.autotest.component.ComponentManager;
import com.yangjie.autotest.context.ContextFactory;
import com.yangjie.autotest.context.ContextIF;
import com.yangjie.autotest.context.GlobalContextProperty;
import com.yangjie.autotest.exception.ActionException;
import com.yangjie.autotest.exception.InitException;
import com.yangjie.autotest.exception.SetupException;
import com.yangjie.autotest.exception.ValidateFailedException;
import com.yangjie.autotest.testcase.xml.Action;
import com.yangjie.autotest.testcase.xml.Setup;
import com.yangjie.autotest.testcase.xml.Step;
import com.yangjie.autotest.testcase.xml.Steps;
import com.yangjie.autotest.testcase.xml.TearDown;
import com.yangjie.autotest.testcase.xml.TestCase;
import com.yangjie.autotest.testcase.xml.TestCases;
import com.yangjie.autotest.testcase.xml.Validate;
import com.yangjie.autotest.testcase.xml.Variable;
import com.yangjie.autotest.testcase.xml.Variables;
import com.yangjie.autotest.util.TestCaseUtils;
import com.yangjie.log.LoggerIF;

@Component
public class TestCaseExecutor {
	@Inject
	private LoggerIF logger = null;

	@Autowired(required = false)
	private List<AfterStepProcessor> afterStepProcessors = null;

	@Autowired(required = false)
	private List<AfterTestCaseProcessor> afterTestCaseProcessors = null;

	@Autowired(required = false)
	private List<PreStepProcessor> preStepProcessors = null;

	@Autowired(required = false)
	private List<PreTestCaseProcessor> preTestCaseProcessors = null;

	@Inject
	private ContextFactory contextFactory = null;

	@Inject
	private ComponentManager componentManager = null;

	private ContextIF context = null;

	public ContextIF getContext() {
		return context;
	}

	public void setContext(ContextIF context) {
		this.context = context;
	}

	public ContextFactory getContextFactory() {
		return contextFactory;
	}

	public void setContextFactory(ContextFactory contextFactory) {
		this.contextFactory = contextFactory;
	}

	



	public int runAction(Action action, ContextIF testCasesContext) throws ActionException {
		logger.debug("Run " + action);
		Object component = componentManager.getComponentInstance(action.getCompName());
		try {
			TestCases testCases = (TestCases) testCasesContext.getProperty(GlobalContextProperty.TESTCASES);
			TestCaseUtils.fillVariables(action.getParams(), testCases.getVariables());
			String[] args = action.getParams().toStringArray();
			Map mapArgs = action.getParams().toMap();
			try {
				MethodUtils.invokeMethod(component, action.getCompMethod(), args);
			} catch(NoSuchMethodException e) {
				try {
					MethodUtils.invokeMethod(component, action.getCompMethod(), new Object[]{action.getParams()});
				} catch(NoSuchMethodException e2) {
				    try {
				        MethodUtils.invokeMethod(component, action.getCompMethod(), new Object[]{context, action.getParams()});
				    } catch(NoSuchMethodException e3) {
				        MethodUtils.invokeMethod(component, action.getCompMethod(), new Object[]{mapArgs});
				    }
				}
			}
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new ActionException(e.getMessage());
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new ActionException(e.getMessage());
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new ActionException(e.getTargetException().getMessage());
		} catch(Exception e) {
			e.printStackTrace();
			throw new ActionException(e.getMessage());
			
		}
//		catch(Exception e) {
//			e.printStackTrace();
//		}
		return ResultConst.SUCC;
	}

	public int runSetup(Setup setup, ContextIF testCasesContext) throws SetupException {
		try {
			if (setup == null) {
				return ResultConst.SUCC;
			}
			List<Action> actionList = setup.getAction();
			for (Action action : actionList) {
				runAction(action, testCasesContext);
			}
		} catch (Exception e) {
			throw new SetupException(e.getMessage());
		}
		return ResultConst.SUCC;
	}

	public int runTearDown(TearDown tearDown, ContextIF testCasesContext) throws SetupException {
		try {
			if (tearDown == null) {
				return ResultConst.SUCC;
			}
			List<Action> actionList = tearDown.getAction();
			for (Action action : actionList) {
				runAction(action, testCasesContext);
			}
		} catch(Exception e) {
			throw new SetupException(e.getMessage());
		}
		return ResultConst.SUCC;
		
	}
	// public void saveVariables(ContextIF testCasesContext, Variables vars) {
	// List<Variable> varList = vars.getVariable();
	// for(Variable var:varList) {
	// testCasesContext.setProperty(var.getName(), var.getValue());
	// }
	// }
	
	public String generateLogMessage(TestCases testCases, TestCase testCase, String message) {
		StringBuffer sb = new StringBuffer();
		if(testCase == null) {
			sb.append("Testcases[").append(testCases.getName()).append("]");
		} else {
			sb.append("  Testcase(").append(testCase.getName()).append(")");
		}
		sb.append(message);
		return sb.toString();
	}
	public void processException(Exception e, ContextIF context) {
		TestCases testCases = (TestCases)context.getProperty(GlobalContextProperty.TESTCASES);
		TestCase testCase = (TestCase)context.getProperty(GlobalContextProperty.CURRENT_TESTCASE);

		if(e instanceof SetupException) {
			logger.info(generateLogMessage(testCases, testCase, " setup failed:" + e.getMessage()));
		} else if(e instanceof ValidateFailedException) {
			logger.info(generateLogMessage(testCases, testCase, " validate failed:" + e.getMessage()));
		} else if(e instanceof InitException) {
			logger.info(generateLogMessage(testCases, testCase, " initialize failed:" + e.getMessage()));
		}else if(e instanceof ActionException) {
			logger.info(generateLogMessage(testCases, testCase, " action failed:" + e.getMessage()));
		} else {
			logger.info(generateLogMessage(testCases, testCase, "  failed:" + e.getMessage()));
		}
	}
	public void setGlobalVariables(TestCases testCases) {
		Variables vars = testCases.getVariables();
		Variable var = new Variable();
		var.setName(GlobalVariablesConstant.VAR_TESTCASE_PATH);
		var.setValue(testCases.getDefinitionFile());
		vars.getVariable().add(var);
	}
	
	public int runTestCases(TestCases testCases, String testCaseName)  {
		logger.info("TestCases[" + testCases.getName() + "] start");
		try {
			setGlobalVariables(testCases);
			context = contextFactory.createTestCasesContext();
			context.setProperty(GlobalContextProperty.TESTCASES, testCases);

			// saveVariables(testCasesContext, testCases.getVariables());

			int result = runSetup(testCases.getSetup(), context);

			List<TestCase> testCaseList = testCases.getTestCase();
			for (TestCase testCase : testCaseList) {
				try {
					if(testCaseName != null) {
						if(testCase.getName().equalsIgnoreCase(testCaseName)) {
							runTestCase(testCase, context);
						}
					} else {
						runTestCase(testCase, context);
					}
					
				} catch(Exception e) {
					e.printStackTrace();
					processException(e, context);
				} 
			}
			runTearDown(testCases.getTearDown(), context);
			logger.info("TestCases[" + testCases.getName() + "] end");
		} catch(Exception e) {
			e.printStackTrace();
			processException(e, context);
		}

		return ResultConst.SUCC;
	}

	public void runValidate(Validate validate, ContextIF testCasesContext) throws ValidateFailedException {
		Object component = componentManager.getComponentInstance(validate.getCompName());
		try {
			TestCases testCases = (TestCases) testCasesContext.getProperty(GlobalContextProperty.TESTCASES);
			TestCaseUtils.fillVariables(validate.getParams(), testCases.getVariables());
			String[] expectedValues = validate.getParams().toStringArray();
			
//			TestCaseUtils.fillVariables(expectedValues, testCases.getVariables());
			
			Object[] args = new Object[expectedValues.length + 1];
			args[0] = context;
			for (int i = 1; i < args.length; i++) {
				args[i] = expectedValues[i - 1];
			}
			try {
				MethodUtils.invokeMethod(component, validate.getCompMethod(), args);
			} catch(NoSuchMethodException e) {
				try {
					MethodUtils.invokeMethod(component, validate.getCompMethod(), new Object[]{context, validate.getParams()});
				} catch(NoSuchMethodException e1) {
					MethodUtils.invokeMethod(component, validate.getCompMethod(), new Object[]{validate.getParams()});
				}
			}
			
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new ValidateFailedException(e.getMessage());
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new ValidateFailedException(e.getMessage());
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
			throw new ValidateFailedException(e.getTargetException().getMessage());
		} catch(Exception e) {
			e.printStackTrace();
			throw new ValidateFailedException(e.getMessage());
		}
		
	}

	public void runValidates(List<Validate> validates, ContextIF testCasesContext) throws ValidateFailedException {
		for (Validate validate : validates) {
			runValidate(validate, testCasesContext);
		}
	}

	public int runStep(Step step, ContextIF testCasesContext) throws ValidateFailedException, ActionException {
		if (preStepProcessors != null) {
			for (PreStepProcessor preStepProcessor : preStepProcessors) {
				preStepProcessor.preStep(testCasesContext, step);
			}
		}
		Action action = step.getAction();
		runAction(action, testCasesContext);
		if (afterStepProcessors != null) {
			for (AfterStepProcessor afterStepProcessor : afterStepProcessors) {
				afterStepProcessor.afterStep(testCasesContext, step);
			}
		}
		runValidates(step.getValidate(), testCasesContext);
		return ResultConst.SUCC;
	}

	public int runSteps(Steps steps, ContextIF testCasesContext) throws ValidateFailedException, ActionException {
		List<Step> stepList = steps.getStep();
		for (Step step : stepList) {
			runStep(step, testCasesContext);
		}
		return ResultConst.SUCC;
	}

	public int runTestCase(TestCase testCase, ContextIF context) throws Exception {
		// ContextIF testCaseContext = contextFactory.createTestCaseContext();
		context.setProperty(GlobalContextProperty.CURRENT_TESTCASE, testCase);
		if (preTestCaseProcessors != null) {
			for (PreTestCaseProcessor preTestCaseProcessor : preTestCaseProcessors) {
				preTestCaseProcessor.preTestCase(context, testCase);
			}
		}

		int result = runSetup(testCase.getSetup(), context);
		runSteps(testCase.getSteps(), context);
		if (afterTestCaseProcessors != null) {
			for (AfterTestCaseProcessor afterTestCaseProcessor : afterTestCaseProcessors) {
				afterTestCaseProcessor.afterTestCase(context, testCase);
			}
		}
		result = runTearDown(testCase.getTearDown(), context);
		// runTearDown(testCase.getTearDown());
		return ResultConst.SUCC;
	}

}
